package org.loong.crypto.service.software.provider.symmetric.aes;

import java.util.Objects;
import java.util.Optional;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang3.ArrayUtils;
import org.loong.crypto.common.constant.CryptoConstants;
import org.loong.crypto.common.exception.CryptoException;
import org.loong.crypto.common.exception.InvalidKeyLengthException;
import org.loong.crypto.core.algorithm.CipherAlgorithm;
import org.loong.crypto.core.params.CipherParameters;
import org.loong.crypto.service.core.provider.Decrypter;
import org.loong.crypto.service.core.provider.impl.AESCipherProvider;

import cn.hutool.crypto.symmetric.SymmetricCrypto;

/**
 * AES decrypter of data.
 */
public class AESDecrypter extends AESCipherProvider implements Decrypter {

    /**
     * The aes decrypt algorithm.
     */
    private String aesAlgorithm;
    
    /**
     * The decrypt iv bytes.
     */
    private byte[] iv;
    
    /**
     * Creates a new AES decrypter.
     *
     * @param keyBytes the Key Encryption Key, as a byte array. Must be 128 bits (16 bytes), 192 bits (24 bytes) or 256
     *            bits (32 bytes). Must not be {@code null}.
     * @throws KeyLengthException If the KEK length is invalid.
     */
    public AESDecrypter(final byte[] keyBytes) throws InvalidKeyLengthException {
        this(new SecretKeySpec(keyBytes, "AES"));
    }

    /**
     * Creates a new AES decrypter.
     *
     * @param secretKey the Key Encryption Key. Must be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes). Must
     *            not be {@code null}.
     * @throws KeyLengthException If the KEK length is invalid.
     */
    public AESDecrypter(final SecretKey secretKey) throws InvalidKeyLengthException {
        super(secretKey);
    }

    @Override
    public void init(CipherParameters parameters) {
        if (Objects.nonNull(parameters.getEncryptionMethod())) {
            this.aesAlgorithm = parameters.getEncryptionMethod().getAlgorithm();
        }
        
        if (ArrayUtils.isNotEmpty(parameters.getIv())) {
            this.iv = parameters.getIv();
        }
    }
    
    @Override
    public byte[] decrypt(CipherAlgorithm algorithm, byte[] cipherText) throws CryptoException {
        String algorithmName = Optional.ofNullable(aesAlgorithm).orElse(algorithm.getName());
        if (algorithmName.contains("NoPadding")) {
            algorithmName = algorithmName.replace("NoPadding", "ZeroPadding");
        }
        
        try {
            SymmetricCrypto ase = new SymmetricCrypto(algorithmName, getSecretKey());
            if (!algorithmName.contains("ECB")) {
                ase.setIv(Optional.ofNullable(iv).orElse(CryptoConstants.DEFAULT_IV));
            }
            return ase.decrypt(cipherText);
        } catch (cn.hutool.crypto.CryptoException e) {
            throw new CryptoException("AES decrypt exception: " + e.getMessage(), e);
        }
    }
}
